package juc.scenarios.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import java.util.Random;

/**
 * 分布式锁演示
 * 使用ReentrantLock + Condition实现简单的分布式锁机制
 */
public class DistributedLockDemo {
    
    private static final Map<String, SimpleDistributedLock> lockRegistry = new ConcurrentHashMap<>();
    private static final AtomicInteger operationCounter = new AtomicInteger(0);
    
    /**
     * 简单的分布式锁实现
     */
    static class SimpleDistributedLock {
        private final ReentrantLock lock;
        private final Condition condition;
        private final String lockName;
        private volatile boolean isLocked = false;
        private volatile Thread currentOwner = null;
        private final AtomicInteger lockCount = new AtomicInteger(0);
        
        public SimpleDistributedLock(String lockName, boolean fair) {
            this.lockName = lockName;
            this.lock = new ReentrantLock(fair);
            this.condition = lock.newCondition();
        }
        
        /**
         * 获取锁
         */
        public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
            lock.lock();
            try {
                long nanos = unit.toNanos(timeout);
                while (isLocked && currentOwner != Thread.currentThread()) {
                    if (nanos <= 0) {
                        return false;
                    }
                    nanos = condition.awaitNanos(nanos);
                }
                
                isLocked = true;
                currentOwner = Thread.currentThread();
                lockCount.incrementAndGet();
                return true;
            } finally {
                lock.unlock();
            }
        }
        
        /**
         * 释放锁
         */
        public void unlock() {
            lock.lock();
            try {
                if (currentOwner == Thread.currentThread()) {
                    lockCount.decrementAndGet();
                    if (lockCount.get() == 0) {
                        isLocked = false;
                        currentOwner = null;
                        condition.signalAll();
                    }
                }
            } finally {
                lock.unlock();
            }
        }
        
        /**
         * 获取锁状态信息
         */
        public LockInfo getLockInfo() {
            return new LockInfo(lockName, isLocked, currentOwner, lockCount.get());
        }
    }
    
    /**
     * 锁信息
     */
    static class LockInfo {
        private final String lockName;
        private final boolean locked;
        private final Thread owner;
        private final int count;
        
        public LockInfo(String lockName, boolean locked, Thread owner, int count) {
            this.lockName = lockName;
            this.locked = locked;
            this.owner = owner;
            this.count = count;
        }
        
        @Override
        public String toString() {
            return String.format("Lock[%s]: locked=%s, owner=%s, count=%d", 
                lockName, locked, owner != null ? owner.getName() : "none", count);
        }
    }
    
    /**
     * 共享资源操作任务
     */
    static class SharedResourceTask implements Runnable {
        private final String resourceId;
        private final String lockName;
        private final int taskId;
        private final Random random = new Random();
        
        public SharedResourceTask(String resourceId, String lockName, int taskId) {
            this.resourceId = resourceId;
            this.lockName = lockName;
            this.taskId = taskId;
        }
        
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println("[" + threadName + "] 任务" + taskId + " 尝试获取锁: " + lockName);
            
            SimpleDistributedLock lock = lockRegistry.computeIfAbsent(lockName, 
                k -> new SimpleDistributedLock(k, true)); // 使用公平锁
            
            try {
                // 尝试获取锁，最多等待3秒
                if (lock.tryLock(3, TimeUnit.SECONDS)) {
                    try {
                        System.out.println("[" + threadName + "] 任务" + taskId + " 成功获取锁");
                        
                        // 模拟对共享资源的操作
                        int operationId = operationCounter.incrementAndGet();
                        System.out.println("[" + threadName + "] 任务" + taskId + " 开始操作共享资源" + resourceId + " (操作ID: " + operationId + ")");
                        
                        // 模拟操作耗时
                        int operationTime = 500 + random.nextInt(1000);
                        Thread.sleep(operationTime);
                        
                        System.out.println("[" + threadName + "] 任务" + taskId + " 完成操作共享资源" + resourceId + "，耗时: " + operationTime + "ms");
                        
                    } finally {
                        lock.unlock();
                        System.out.println("[" + threadName + "] 任务" + taskId + " 释放锁");
                    }
                } else {
                    System.out.println("[" + threadName + "] 任务" + taskId + " 获取锁超时，放弃操作");
                }
                
            } catch (InterruptedException e) {
                System.out.println("[" + threadName + "] 任务" + taskId + " 被中断");
                Thread.currentThread().interrupt();
            }
        }
    }
    
    /**
     * 公平锁与非公平锁对比演示
     */
    static class FairnessComparisonDemo {
        
        public static void demonstrateFairness() throws InterruptedException {
            System.out.println("\n=== 公平锁与非公平锁对比演示 ===");
            
            // 公平锁演示
            System.out.println("\n--- 公平锁演示 ---");
            SimpleDistributedLock fairLock = new SimpleDistributedLock("fair-lock", true);
            demonstrateLockBehavior(fairLock, "公平锁");
            
            Thread.sleep(1000); // 等待公平锁演示完成
            
            // 非公平锁演示
            System.out.println("\n--- 非公平锁演示 ---");
            SimpleDistributedLock unfairLock = new SimpleDistributedLock("unfair-lock", false);
            demonstrateLockBehavior(unfairLock, "非公平锁");
        }
        
        private static void demonstrateLockBehavior(SimpleDistributedLock lock, String lockType) 
                throws InterruptedException {
            
            // 创建多个线程竞争锁
            Thread[] threads = new Thread[5];
            for (int i = 0; i < threads.length; i++) {
                final int threadId = i;
                threads[i] = new Thread(() -> {
                    try {
                        if (lock.tryLock(2, TimeUnit.SECONDS)) {
                            try {
                                System.out.println("[" + Thread.currentThread().getName() + "] " + 
                                    lockType + " - 线程" + threadId + " 获取到锁");
                                Thread.sleep(100); // 短暂持有锁
                            } finally {
                                lock.unlock();
                            }
                        } else {
                            System.out.println("[" + Thread.currentThread().getName() + "] " + 
                                lockType + " - 线程" + threadId + " 获取锁超时");
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }, "Thread-" + threadId);
            }
            
            // 启动所有线程
            for (Thread thread : threads) {
                thread.start();
            }
            
            // 等待所有线程完成
            for (Thread thread : threads) {
                thread.join();
            }
        }
    }
    
    /**
     * 条件变量使用演示
     */
    static class ConditionDemo {
        private final ReentrantLock lock = new ReentrantLock();
        private final Condition condition = lock.newCondition();
        private volatile boolean ready = false;
        
        public void demonstrateCondition() throws InterruptedException {
            System.out.println("\n=== 条件变量使用演示 ===");
            
            // 消费者线程
            Thread consumer = new Thread(() -> {
                lock.lock();
                try {
                    System.out.println("[消费者] 等待条件满足...");
                    while (!ready) {
                        condition.await();
                    }
                    System.out.println("[消费者] 条件满足，继续执行");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    lock.unlock();
                }
            }, "Consumer");
            
            // 生产者线程
            Thread producer = new Thread(() -> {
                try {
                    Thread.sleep(1000); // 模拟准备时间
                    lock.lock();
                    try {
                        System.out.println("[生产者] 设置条件为true");
                        ready = true;
                        condition.signal(); // 通知等待的线程
                        System.out.println("[生产者] 发送信号通知消费者");
                    } finally {
                        lock.unlock();
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, "Producer");
            
            consumer.start();
            producer.start();
            
            consumer.join();
            producer.join();
        }
    }
    
    /**
     * 主方法：运行分布式锁演示
     */
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== 分布式锁演示开始 ===");
        System.out.println("演示时间: " + new java.util.Date());
        System.out.println();
        
        // 1. 基本分布式锁演示
        System.out.println("--- 基本分布式锁演示 ---");
        demonstrateBasicDistributedLock();
        
        Thread.sleep(2000);
        
        // 2. 公平锁与非公平锁对比
        FairnessComparisonDemo.demonstrateFairness();
        
        Thread.sleep(2000);
        
        // 3. 条件变量演示
        ConditionDemo conditionDemo = new ConditionDemo();
        conditionDemo.demonstrateCondition();
        
        System.out.println("\n=== 分布式锁演示结束 ===");
        
        // 打印锁注册表状态
        System.out.println("\n=== 锁状态统计 ===");
        lockRegistry.forEach((name, lock) -> {
            System.out.println(lock.getLockInfo());
        });
    }
    
    /**
     * 基本分布式锁演示
     */
    private static void demonstrateBasicDistributedLock() throws InterruptedException {
        String resourceId = "shared-resource-1";
        String lockName = "resource-lock-1";
        
        System.out.println("共享资源ID: " + resourceId);
        System.out.println("锁名称: " + lockName);
        System.out.println();
        
        // 创建多个线程同时访问共享资源
        int threadCount = 8;
        Thread[] threads = new Thread[threadCount];
        
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(new SharedResourceTask(resourceId, lockName, i), "Worker-" + i);
        }
        
        // 启动所有线程
        for (Thread thread : threads) {
            thread.start();
        }
        
        // 等待所有线程完成
        for (Thread thread : threads) {
            thread.join();
        }
        
        System.out.println("\n总操作次数: " + operationCounter.get());
    }
}